package 反射;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
    调用运行时类的指定结构
        1.调用指定方法
            通过反射,调用类中的方法,通过Method类完成,步骤:
                (1)通过Class类的getMethod(String name,Class…parameterTypes):方法取得一个Method对象,并设置此方法操作时所需要的参数类型
                (2)之后使用Object invoke(Object obj, Object[] args):进行调用,并向方法中传递要设置的obj对象的参数信息
                (3)Object invoke(Object obj, Object …  args)
                   说明:
                         Object 对应原方法的返回值,若原方法无返回值,此时返回null
                         若原方法若为静态方法,此时形参Object obj可为null
                         若原方法形参列表为空,则Object[] args为null
                         若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法
        2.调用指定属性
              在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和 get()方法就可以完成设置和取得属性内容的操作
                   public Field getField(String name):返回此Class对象表示的类或接口的指定的 public的Field
                   public Field getDeclaredField(String name):返回此Class对象表示的类或接口的指定的Field
              在Field中
                   public Object get(Object obj):取得指定对象obj上此Field的属性内容
                   public void set(Object obj,Object value):设置指定对象obj上此Field的属性内容
        3.关于setAccessible方法的使用
               Method和Field、Constructor对象都有setAccessible()方法
               setAccessible启动和禁用访问安全检查的开关
               参数值为true则指示反射的对象在使用时应该取消Java语言访问检查
                    提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么则设置为true
                    使得原本无法访问的私有成员也可以访问
               参数值为false则指示反射的对象应该实施Java语言访问检查
 */
public class Class07 {

    //调用运行时类中指定的属性 方式一(一般不用)
    @Test
    public void FieldTest01() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class<People> peopleClass = People.class;

        //创建运行时类的对象  --> 非静态属性依赖于对象
        People people = peopleClass.newInstance();

        Field id = peopleClass.getField("id"); //要求运行时类中属性声明为public,但属性一般不会声明为public,所以该方法不常使用
        //设置当前属性的值
        id.set(people,1001);  //参数1:指明设置哪个对象的属性  参数2:将此属性值设置为多少
        //获取当前属性的值
        int pId = (int) id.get(people);
        System.out.println(pId);
    }

    //调用运行时类中指定的属性 方式二(主要使用)
    @Test
    public void FieldTest02() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class<People> peopleClass = People.class;

        //创建运行时类的对象  --> 非静态属性依赖于对象
        People people = peopleClass.newInstance();

        //getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
        Field name = peopleClass.getDeclaredField("name");

        //保证当前属性是可访问的,该步骤一定要有,否则如果属性权限不为public,则会报非法访问异常
        name.setAccessible(true);

        //设置当前属性的值  //如果是静态属性,则赋为类即可,参照调用静态方法
        name.set(people,"Tom");
        //获取当前属性的值
        String pName = (String)name.get(people);
        System.out.println(pName);
    }

    //调用运行时类中指定的方法(掌握)
    @Test
    public void MethodTest03() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<People> peopleClass = People.class;

        //创建运行时类的对象  --> 非静态属性依赖于对象
        People people = peopleClass.newInstance();

        /*
            getDeclaredMethod(): 参数一:指明获取的方法的名称 参数二:指明获取的方法的形参列表(重载的方法可能有多个,通过该参数具体到某个方法)
         */
        Method show = peopleClass.getDeclaredMethod("show", String.class);

        //保证当前属性是可访问的,该步骤一定要有,否则如果属性权限不为public,则会报非法访问异常
        show.setAccessible(true);

        /*
            invoke(): 参数一:方法的调用者 参数二:给方法形参赋值的实参
                invoke()方法的返回值即为对应类中调用该方法的返回值
         */
        Object returnValue = show.invoke(people, "中国");
        System.out.println(returnValue);

        System.out.println("********调用静态方法********");

        Method showDesc = peopleClass.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        Object invoke = showDesc.invoke(People.class);
        System.out.println(invoke); //如果方法没有返回值,则返回null
    }

    //调用运行时类中指定的构造器 --> 不常用,一般都使用无参构造器,然后通过方法赋值,通用性高
    @Test
    public void ConstructorTest04() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<People> peopleClass = People.class;

        //getDeclaredConstructor(): 参数:指明构造器的参数列表
        Constructor<People> declaredConstructor = peopleClass.getDeclaredConstructor(String.class);

        //保证此构造器是可访问的
        declaredConstructor.setAccessible(true);

        //调用此构造器创建运行时类的对象
        People people = declaredConstructor.newInstance("Jack");
        System.out.println(people);
    }


}
